package scheduler;

import change.Revision;
import com.gargoylesoftware.htmlunit.SgmlPage;
import commonutils.MyCollectionUtils;
import commonutils.MyWikiObjectUtils;
import main.ChangesDepot;
import main.DatabaseChangesDepot;
import page.Lang;
import page.Link;
import page.WikiPage;
import pageblock.PageBlock;
import parser.WikiPageParser;
import parser.WikiPageParserImpl;
import robot.Robot;
import typeresolver.ChangesDetector;

import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Created by IntelliJ IDEA.
 * User: Evgeniy Krasko
 * Date: 01.12.11
 * Time: 23:38
 */
public class Scheduler {
    public static final Logger logger = Logger.getLogger(Scheduler.class.getName());
    public static final int BLOCKS_BATCH_SIZE = 50;

    static {
        logger.setLevel(Level.ALL);
    }

    private final Robot robot = new Robot();
    private final WikiPageParser parser = new WikiPageParserImpl();
    private final ChangesDepot depot = new DatabaseChangesDepot();


    public void saveChangesToStorage(final Date from, final Date to) {

        logger.info("Starting saving changes to storage for range: " + from + " " + to);

        final List<Revision> allNewRevisions = new ArrayList<Revision>();
        for (final Lang lang : Lang.values()) {
            allNewRevisions.addAll(robot.getChangedPages(lang, from, to));
        }
        final List<WikiPage> newPages = MyWikiObjectUtils.getPagesFromRevisions(allNewRevisions);
        final List<Link> links = robot.getInterlinks(newPages);

        final Set<WikiPage> pagesHavingLinks = MyCollectionUtils.asSet(MyWikiObjectUtils.getPagesByLinks(links));
        newPages.retainAll(pagesHavingLinks);
        MyWikiObjectUtils.retainAll(allNewRevisions, pagesHavingLinks);
        
        logger.info("Downloaded revisions count: " + allNewRevisions.size());

        final List<PageBlock> newPageBlocks = receivePageBlocksByRevisions(allNewRevisions);
        final List<PageBlock> oldPageBlocks = depot.getChanges(newPages);

        logger.info("New page block count: " + newPageBlocks.size());
        logger.info("Old page block count: " + oldPageBlocks.size());

        final List<PageBlock> trulyNewBlocks = filterPageBlocks(newPageBlocks, oldPageBlocks);

        logger.info("Saving to storage: " + trulyNewBlocks.size());
        depot.submit(trulyNewBlocks);
        depot.resolveInterlinks(links);
    }

    private List<PageBlock> filterPageBlocks(List<PageBlock> newPageBlocks, List<PageBlock> oldPageBlocks) {
        final Map<Object, PageBlock> toNewPageBlocks = new HashMap<Object, PageBlock>();
        for (final PageBlock newPageBlock : newPageBlocks) {
            toNewPageBlocks.put(getKey(newPageBlock), newPageBlock);
        }
        for (final PageBlock oldPageBlock : oldPageBlocks) {
            final PageBlock newPageBlock = toNewPageBlocks.get(getKey(oldPageBlock));
            if (!ChangesDetector.blockChanged(oldPageBlock, newPageBlock)) {
                toNewPageBlocks.remove(getKey(oldPageBlock));
            }
        }
        return new ArrayList<PageBlock>(toNewPageBlocks.values());
    }


    private List<PageBlock> receivePageBlocksByRevisions(final List<Revision> revisions) {
        final List<PageBlock> resultBlocks = new ArrayList<PageBlock>();
        for (List<Revision> revisionsBatch : MyCollectionUtils.splitList(revisions, BLOCKS_BATCH_SIZE)) {
            final Map<Revision, SgmlPage> revToPages = robot.getHtmlPages(revisionsBatch);
            for (final Revision revision : revToPages.keySet()) {
                resultBlocks.addAll(parser.parse(revToPages.get(revision), revision));
            }
            logger.info("Processed a batch of revisions, starting from " + revisionsBatch.get(0).getRevId());
        }
        return resultBlocks;
    }

    private Object getKey(final PageBlock block) {
        return MyCollectionUtils.asListWithoutNulls(block.getType(), block.getRevision().getWikiPage());
    }
}
